/**
 * 跨操作系统统一接口
 * 张勇 41204@qq.com
 */

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>

#include "cccompat.h"
#include "std_err.h"

// forward 声明
// 数据通信收发器
typedef struct _transceiver transceiver_t;

#ifdef __cplusplus
extern "C" {
#endif

// ======================================================================================
// 基础功能
// ======================================================================================

/**
 * 关于内存管理
 * - windows, linux 系统下使用 malloc(), free() 实现
 * - freertos 系统下使用 pvPortMalloc(), vPortFree() 实现
 */
/// @brief 分配内存
void* unios_malloc(size_t size);
/// @brief 释放内存
void unios_free(void *ptr);
/// @brief 从一个遵从本机字节序的字节流中，读取一个指针，解决字节对齐问题，同时解决字节序问题
void* unios_get_pointer(const void *stream);

/// 计算结构体中指定字段的偏移地址
#ifndef FIELD_OFFSET
#define FIELD_OFFSET(type, field) ((size_t)&(((type*)0)->field))
#endif

// 把结构/变量清零
#define ZEROVAR(v)  memset(&(v), 0, sizeof(v))
// 把指针指向的变量清零
#define ZEROPTR(p)  memset(p, 0, sizeof(p[0]))
// 把数组清零
#define ZEROARR(a)  memset(a, 0, sizeof(a))

// 分配指定大小的内存，并把它清零
void* unios_malloc0(size_t size);
#define malloc0(x)  unios_malloc0(x)
// 分配指定类型的内存
#define MALLOC(t)	((t*)unios_malloc(sizeof(t)))
// 分配指定类型的内存，并把它清零
#define MALLOC0(t)	((t*)unios_malloc0(sizeof(t)))
// 释放指针指向的内存，并将指针置为 NULL
#define FREEPTR(p)  do { if(p) { unios_free((void*)(p)); p = NULL; } } while(0)
// 释放字符串数组(最一个字符串要是 NULL)，并将指针置为 NULL
#define FREESTRARR(sa)  do {                                       \
    if(sa) {                                                       \
        for(void **pp = (void**)(sa); *pp; unios_free(*pp), pp++); \
        unios_free((void*)(sa)); sa = NULL;                        \
    }                                                              \
} while(0)

//
// 关于中断管理
//
/// @brief 关闭所有中断
void unios_disable_interrupts();
/// @brief 打开所有中断
void unios_enable_interrupts();

/// @brief 当前是否在中断中
bool unios_is_in_isr();

/// @brief 在关闭中断的状态下执行代码
#define unios_exec_withou_interrupt(x)	do { unios_disable_interrupts(); (x); unios_enable_interrupts(); } while(0)

//
// 关于时钟管理
//

// us 时间戳
typedef uint64_t unios_us_t;
/// @brief 获取当前系统时钟微秒数
unios_us_t unios_us_now();
/// @brief 过去了多少 us
unios_us_t unios_us_elapsed(unios_us_t earlier_time_us);
/// @brief 延时us（如果有操作系统，当前任务不会挂起）
void unios_us_delay(unios_us_t delay);

// ms 时间戳
typedef uint64_t unios_ms_t;
/// @brief 获取当前系统时钟毫秒数
unios_ms_t unios_ms_now();
/// @brief 计算已经逝去的时间毫秒数
unios_ms_t unios_ms_elapsed(unios_ms_t earlier_time_ms);
/// @brief 延时ms（如果有操作系统，则当前任务会挂起，让出时间片）
void unios_ms_delay(unios_ms_t delay);

/// @brief 喂看门狗
void unios_watchdog_feed(void);

/// 时间
typedef struct _unios_time {
	uint8_t hour;
	uint8_t minute;
	uint8_t second;
} unios_time_t;

/// 时间段
typedef struct _unios_time_period {
	unios_time_t start;
	unios_time_t end;
} unios_time_period_t;

/// 日期
typedef struct _unios_date {
	uint16_t year;
	uint8_t month;
	uint8_t day;
	uint8_t weekday;
} unios_date_t;

/// 日期&时间
typedef struct _unios_datetime {
	unios_date_t date;
	unios_time_t time;
} unios_datetime_t;

// second 时间戳(自1970-1-1 00:00:00至当前的秒数, 32 位时间可以表示到 2106 年，够用了 :)
typedef uint32_t unios_sec_t;
/// @brief 获得当前时间(自1970-1-1 00:00:00至当前的秒数)
unios_sec_t unios_rtcsec_get();
/// @brief 获得当前时间(当天的秒数)
static inline unios_sec_t unios_rtcsec_in_day() { return unios_rtcsec_get() % (24 * 60 * 60); }
/// @brief 过去了多少 second
unios_sec_t unios_rtcsec_elapsed(unios_sec_t earlier_time_sec);
/// @brief 设置当前时间(自1970-1-1 00:00:00至当前的秒数)
std_err_t unios_rtcsec_set(unios_sec_t sec);
/// @breif 设置时间
std_err_t unios_rtc_set(int year, int month, int day, int hour, int minute, int second);
/// @breif 获取时间
std_err_t unios_rtc_get(int *year, int *month, int *day, int *hour, int *minute, int *second, int *weekday);
/// @brief 设置当前时区(tz 格式定义与 setenv("TZ"), tzset() 要求相同)
std_err_t unios_rtc_timezone_set(const char *tz);
/// @breif 获取 RTC 当前时区的时间
unios_datetime_t unios_rtc_local();
/// @brief 获得 RTC 当前时区的时间字符串
/// 注意: 本函数使用了静态字符串缓冲区，不是多任务安全的！应在函数返回后拷贝到自己的缓冲区保存！
char* unios_rtc_local_string();

/// @brief 当 exp 为 true 时，一直等待，直至 timeout 超时，ret = 0; 如果等待过程中 exp 变为 false 则立即返回，ret 为花费的时间
#define __WAIT_WHILE__(ret, timeout, exp, fnow, fet) \
	do {                                        	\
		unios_us_t et, start = fnow();          	\
		while(1) {                              	\
			et = fet(start);                    	\
			if(et >= timeout) { ret = 0; break; }	\
			if(!(exp)) { ret = et ? et : 1; break; }\
			unios_watchdog_feed();              	\
		};                                      	\
	} while(0)

/// @brief while: 当 ... 时等待 / until: 等待直到 ...
/// @param var: 变量名
/// @param time: 等待超时时间(毫秒/微秒)
/// @param express: 结束条件
/// @return: var=0 表示等待超时；var>0 表示正常，值为等待的时间
#define unios_wait_us_while(var, time, express)	__WAIT_WHILE__((var), (time),  (express), unios_us_now, unios_us_elapsed)
#define unios_wait_us_until(var, time, express)	__WAIT_WHILE__((var), (time), !(express), unios_us_now, unios_us_elapsed)
#define unios_wait_ms_while(var, time, express)	__WAIT_WHILE__((var), (time),  (express), unios_ms_now, unios_ms_elapsed)
#define unios_wait_ms_until(var, time, express)	__WAIT_WHILE__((var), (time), !(express), unios_ms_now, unios_ms_elapsed)


// ======================================================================================
// 关于文件
// ======================================================================================

/**
 * 文件句柄数据类型
 * - windows 系统下使用 Win32 文件系统: CreateFile(), CloseHandle(), ReadFile(), WriteFile(), SetFilePointerEx(), GetFileSizeEx(), DeviceIoControl()
 * - linux 系统下使用 posix 文件系统: open(), close(), read(), write(), lseek64(), ioctl(), mmap(), munmap()
 */
#ifdef _WIN32
    typedef void* unios_file_t;
#elif (__linux__ || __unix__)
    typedef int   unios_file_t;
#else
    typedef uintptr_t unios_file_t;
#endif

/// 文件打开标志
typedef enum _unios_file_oflag {
	UNIOS_FILE_OFLAG_NULL = 0,
	UNIOS_FILE_OFLAG_READ,			//< 只读方式打开，文件必须已经存在。
	UNIOS_FILE_OFLAG_APPEND,		//< 添加方式打开，如果文件不存在，则创建文件。
	UNIOS_FILE_OFLAG_WRITE_TRUNCATE,//< 只写方式打开，如果文件不存在，则创建文件；如果文件已经存在，打开后清空文件数据。
	UNIOS_FILE_OFLAG_READ_WRITE,	//< 读写方式打开，文件必须已经存在。
	UNIOS_FILE_OFLAG_DEVICE_IO,		//< 以设备驱动文件方式打开，打开后可读写控制设备。
	UNIOS_FILE_OFLAG_MAX
} unios_file_oflag_t;

/// 文件打开标志是否有效
inline bool unios_file_oflag_is_valid(unios_file_oflag_t oflag) { return oflag < UNIOS_FILE_OFLAG_MAX; }

/**
 * 返回一个无效的文件句柄，用于初始化一个文件句柄
 * @return 无效的文件句柄
 */
unios_file_t unios_file_null();

/**
 * 判断文件是否是有效的
 * @return 是否有效
 */
bool unios_file_is_valid(unios_file_t uf);

/**
 * 把 C 语言标准 I/O 流式文件指针转换为对应的 unios_file 句柄。Windows 系统下转换为 HANDLE; Linux 系统下转换为 int 文件描述符。
 * @param fp C 语言标准 I/O 流式文件指针
 * @return unios_file 句柄。此句柄在使用完成后不要用 unios_file_close() 关闭，而是要用 fclose(fp) 关闭 !
 */
unios_file_t unios_file_from_stdio_stream(FILE *fp);

/**
 * 获取文件当前大小
 * @param path 文件路径
 * @return >=0 时表示文件当前大小。<0 时表示出错。
 */
int64_t unios_file_size(const char *path);

/**
 * 判断文件是否存在
 * @param path 文件路径
 * @return 文件是否存在
 */
 static inline bool unios_file_exist(const char *path) { return (unios_file_size(path) >= 0);}

/**
 * 判断文件是否存在
 * @param from 文件改名前的路径
 * @param to 文件改名后的路径
 * @return 标准错误码。
 */
std_err_t unios_file_rename(const char *from, const char *to);

/**
 * 删除文件
 * @param path 文件路径
 * @return 标准错误码。
 */
std_err_t unios_file_delete(const char *path);

/**
 * 打开文件
 * @param path 文件路径
 * @param oflag 文件打开标志。
 * @param uf [out] 文件句柄指针。
 * @return 标准错误码。
 */
std_err_t unios_file_open(const char *path, unios_file_oflag_t oflag, unios_file_t *uf);

/**
 * 关闭文件同时把文件句柄设置为无效
 * @param uf [out] 文件句柄指针
 */
void unios_file_destroy(unios_file_t *uf /*in,out*/);

/**
 * 关闭文件
 * @param uf 文件句柄
 */
static inline void unios_file_close(unios_file_t uf) { unios_file_destroy(&uf); }

/**
 * 获取文件当前大小
 * @param uf 文件句柄
 * @return >=0 时表示文件当前大小。<0 时表示出错。
 */
int64_t unios_file_datasize(unios_file_t uf);

/**
 * 确保文件缓存与实际物理存储数据一致
 * @param uf 文件句柄
 * @return 标准错误码。
 */
std_err_t unios_file_sync(unios_file_t uf);

/**
 * 读取文件
 * @param uf 文件句柄
 * @param buf 要读取的数据缓冲区
 * @param nbyte 要读取的数据长度
 * @param offset 读取位置距离文件头位置的偏移地址。<0 时，不设置偏移地址（有的文件不支持设置偏移）。
 * @param rbyte [out] 实际读取的有效数据长度
 * @return 标准错误码。
 */
std_err_t unios_file_read(unios_file_t uf, void *buf, size_t nbyte, int64_t offset, size_t *rbyte);

/**
 * 写入文件
 * @param uf 文件句柄
 * @param buf 要写入的数据缓冲区
 * @param nbyte 要写入的数据长度
 * @param offset 写入位置距离文件头位置的偏移地址。<0 时，不设置偏移地址（有的文件不支持设置偏移）。
 * @param wbyte [out] 实际写入的有效数据长度
 * @return 标准错误码。
 */
std_err_t unios_file_write(unios_file_t uf, const void *buf, size_t nbyte, int64_t offset, size_t *wbyte);

/**
 * 对文件做内存映射
 * @param uf 文件句柄
 * @param offset 要映射的区域到文件头部的偏移（字节）
 * @param size 要映射的区域大小（字节）
 * @param flags1 文件系统相关的映射标志1。linux 系统做为 mmap() 的 prot 参数。
 * @param flags2 文件系统相关的映射标志2。linux 系统做为 mmap() 的 flags 参数。
 * @param mmptr [out] 实际映射的内存起始位置指针（用于解除映射）
 * @param mmsize [out] 实际映射的内存大小（字节）（用于解除映射）
 * @return 映射后的有效数据指针。NULL 表示失败。
 */
void* unios_file_mmap(unios_file_t uf, uint64_t offset, uint64_t size, uint64_t flags1, uint64_t flags2, void **mmptr/*out*/, uint64_t *mmsize/*out*/);

/**
 * 解除文件内存映射
 */
void unios_file_munmap(void *mmptr, uint64_t mmsize);

/**
 * 设备文件 I/O Control
 * @param uf 文件句柄
 * @param cmd 要控制的功能命令字。根据操作系统和文件不同，支持的功能可能是不同的。linux 请参考 ioctl() 函数 request 参数；windows 请参考 DeviceIoControl() 函数 dwIoControlCode 参数。
 * @param opt 功能可选参数指针。不同的命令字所需要的参数也是不同的。
 * @param opt_size 功能可选参数大小。
 * @return -1 表示失败。其它值根据 cmd 不同定义可能不同。
 */
int unios_file_ioctl(unios_file_t uf, int cmd, void *opt, size_t opt_size);

/**
 * 把内存块存储到文件中
 */
std_err_t unios_file_save(const char *filepath, const void *mem, size_t size);

/**
 * 加载文件数据到内存块中
 * @param filepath 文件路径
 * @param mem [out] 分配的内存地址
 * @param size [out] 分配的内存大小
 * @return 标准错误码
 */
std_err_t unios_file_load(const char *filepath, void **mem, size_t *size);


// ======================================================================================
// 关于 CPU
// ======================================================================================

/**
 * 获取 CPU 内核数量
 * @return >0 表示内核数量；=0 表示遇到错误。
 */
size_t unios_get_cpu_core_count();


// ======================================================================================
// 关于线程
// ======================================================================================

/**
 * 线程优先级
 */
typedef enum {
    UNIOS_THREAD_PRIORITY_DEFAULT = 0,
    UNIOS_THREAD_PRIORITY_IDLE,
    UNIOS_THREAD_PRIORITY_LOWEST,
    UNIOS_THREAD_PRIORITY_BELOW_NORMAL,
    UNIOS_THREAD_PRIORITY_NORMAL,
    UNIOS_THREAD_PRIORITY_ABOVE_NORMAL,
    UNIOS_THREAD_PRIORITY_HIGHEST,
    UNIOS_THREAD_PRIORITY_TIME_CRITICAL,
    UNIOS_THREAD_PRIORITY_MAX
} unios_thread_priority_t;

/**
 * 线程状态标志
 */
typedef enum {
    UNIOS_THREAD_STATE_NONE = 0,  // 线程未创建
    UNIOS_THREAD_STATE_STARTING,  // 线程正在启动
    UNIOS_THREAD_STATE_RUNNING,   // 线程正在运行
    UNIOS_THREAD_STATE_SUSPEND,   // 线程被挂起
    UNIOS_THREAD_STATE_EXITCMD,   // 线程退出指令
    UNIOS_THREAD_STATE_EXITING,   // 线程正在退出
    UNIOS_THREAD_STATE_MAX
} unios_thread_state_t;

/**
 * 线程句柄
 */
typedef void* unios_thread_t;

/**
 * 线程处理函数原型
 */
typedef void (*unios_thread_func_t)(void *cookie);

/**
 * 创建线程
 * @param func 线程处理函数
 * @param func 线程处理函数
 * @param cookie [in] 线程启动时传给线程的参数
 * @param priority 线程优先级
 * @param stack_size 线程堆栈大小
 * @param cpu_affinity 指定线程可在哪些 CPU 内核上运行（对应的内核 bit 设置为 1）。=0 时不绑定 CPU 内核，由操作系统分配。
 * @param pt [out] 指向线程句柄的指针
 * @return 标准错误码
 */
std_err_t unios_thread_create(unios_thread_func_t func, void *cookie, unios_thread_priority_t priority, size_t stack_size, uint64_t cpu_affinity, unios_thread_t* pt);

/**
 * 关闭线程
 * @param thread unios_thread_create() 返回的线程句柄。传 NULL 表示关闭调用者线程，任务会立即终止，剩余代码不会执行。
 * @attention 在 FreeRTOS 中线程退出前必须调用此函数释放线程资源！
 * @attention 在 FreeRTOS 中在中断处理程序中不允许删除线程，调用此函数不执行任何操作！
 */
void unios_thread_close(unios_thread_t thread);
static inline void unios_thread_destroy(unios_thread_t *pthread) { if(pthread) { unios_thread_close(*pthread); *pthread = NULL; } }

/**
 * 返回调用此函数的线程句柄
 * @return 成功返回线程句柄；失败返回 NULL
 * @note 注意: 在 windows 系统下，返回的是一个当前线程的“伪句柄”，只在本线程中有效！也不能用于 CloseHandle() !
 */
unios_thread_t unios_thread_this();


// ======================================================================================
// 关于临界区
// 临界区区分线程: 一个线程进入临界区之后，本线程再次进入不会被阻塞，而其它线程尝试进入时会被阻塞 !
// ======================================================================================

/**
 * 临界区句柄
 * - windows 系统下使用 Win32 的 CRITICAL_SECTION 相关 API 实现: EnterCriticalSection(), TryEnterCriticalSection(), LeaveCriticalSection()
 * - linux 系统下使用 pthread_mutex_t 相关 API 实现: pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock()
 * - freertos 系统下使用 SemaphoreHandle_t 相关 API 实现: xSemaphoreCreateBinary(), xSemaphoreTake(), xSemaphoreGive(), vSemaphoreDelete()
 * - 单片机裸机 系统下使用 全局中断管理 相关 API 实现: mcu_disable_interrupts(), mcu_enable_interrupts(), mcu_get_interrupts_state(), mcu_set_interrupts_state(mcu_istate_t state)
 */
typedef void* unios_critical_section_t;

/**
 * 初始化临界区
 * @param pcs [out] 指向临界区的指针
 * @return 标准错误码
 */
std_err_t unios_critical_section_init(unios_critical_section_t* pcs);

/**
 * 进入临界区
 * @param cs 临界区句柄
 * @param wait_timeout_ms 等待时间（ms）。<0 则永远等待；=0 则不等待
 * @return 标准错误码。如果在指定时间内未进入，则返回 STD_ERR_TIMEOUT。
 */
std_err_t unios_critical_section_enter(unios_critical_section_t cs, long wait_timeout_ms);

/**
 * 退出临界区
 * @param cs 临界区句柄
 */
void unios_critical_section_leave(unios_critical_section_t cs);

/**
 * 关闭临界区，同时把临界区句柄设置为无效
 * @param cs 临界区句柄指针
 */
void unios_critical_section_destroy(unios_critical_section_t *cs/*in,out*/);

/**
 * 关闭临界区，释放相关资源
 * @param cs 临界区句柄
 */
static inline void unios_critical_section_close(unios_critical_section_t cs) { if(cs) unios_critical_section_destroy(&cs); }

/**
 * 在临界区中执行代码
 * @param cs 临界区句柄
 * @param wait_timeout_ms 等待时间（ms）。<0 则永远等待；=0 则不等待
 * @param codes 要执行的代码
 */
#define unios_critical_section_do(cs, wait_timeout_ms, codes) do { \
    unios_critical_section_enter(cs, wait_timeout_ms);             \
    codes                                                          \
    unios_critical_section_leave(cs);                              \
} while(0)


// ======================================================================================
// 关于互斥锁
// 互斥锁有数量: 在初始化时指定锁的数量，可以被多次锁定，每次数量减1；数量减为0后再次尝试锁定会被阻塞 !
// 互斥锁不区分线程: 不管哪个线程锁定之后，本线程或其它线程尝试再次锁定时都会被阻塞 !
// ======================================================================================

/**
 * 互斥锁句柄
 * - windows 系统下使用 Win32 的 Semaphore 相关 API 实现: CreateSemaphore(), CloseHandle(), WaitForSingleObject(), ReleaseSemaphore()
 * - linux 系统下使用 pthread_cond_t + pthread_mutex_t 组合实现: pthread_cond_init(), pthread_cond_wait(), pthread_cond_signal()/pthread_cond_broadcast(), pthread_mutex_lock(), pthread_mutex_unlock()
 */
typedef void* unios_lock_t;

/**
 * 初始化互斥锁
 * @param pcs [out] 指向互斥锁句柄的指针
 * @param count 互斥锁计数器最大值。每次 unios_lock() 成功时减 1，unios_unlock() 成功时加 1。
 * @return 标准错误码
 */
std_err_t unios_lock_init(unios_lock_t* plock, size_t count);

/**
 * 获取互斥锁
 * @param lock 互斥锁句柄
 * @param wait_timeout_ms 等待时间（ms）。<0 则永远等待；=0 则不等待
 * @return 标准错误码。如果在指定时间内未获取互斥锁，则返回 STD_ERR_TIMEOUT。
 */
std_err_t unios_lock(unios_lock_t lock, long wait_timeout_ms);

/**
 * 释放互斥锁
 * @param lock 互斥锁句柄
 * @return 标准错误码
 */
std_err_t unios_unlock(unios_lock_t lock);

/**
 * 关闭互斥锁同时把互斥锁句柄设置为无效
 * @param lock [in,out] 互斥锁句柄指针
 */
void unios_lock_destroy(unios_lock_t *lock /*in,out*/);

/**
 * 关闭互斥锁，释放相关资源
 * @param lock 互斥锁句柄
 */
static inline void unios_lock_close(unios_lock_t lock) { unios_lock_destroy(&lock); }


// ======================================================================================
// 关于事件
// ======================================================================================

/**
 * 事件句柄
 * - windows 系统下使用 Win32 的 Event 相关 API 实现: CreateEvent(), CloseHandle(), WaitForSingleObject(), SetEvent()/ResetEvent()
 * - linux 系统下使用 pthread_mutex_t 相关 API 实现: pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock()
 */
typedef void* unios_event_t;

/**
 * 初始化事件
 * @param pevent [out] 指向事件句柄的指针
 * @param manual_reset 是否需要手动复位信号。false 时会在 unios_event_wait() 正常返回后自动将事件置为无信号。
 * @param init_state 初始化后是否默认设置为有信号
 * @return 标准错误码
 */
std_err_t unios_event_init(unios_event_t* pevent, bool manual_reset, bool init_state);

/**
 * 设置事件是否有信号
 * @param event 事件句柄
 * @param signal 是否有信号
 * @return 标准错误码。
 */
std_err_t unios_event_set(unios_event_t event, bool signal);

/**
 * 等待事件变为有信号
 * @param event 事件句柄
 * @param wait_timeout_ms 等待时间（ms）。<0 则永远等待；=0 则不等待
 * @return 标准错误码。
 */
std_err_t unios_event_wait(unios_event_t event, long wait_timeout_ms);

/**
 * 关闭事件同时把事件句柄设置为无效
 * @param pevent [in,out] 事件句柄指针
 */
void unios_event_destroy(unios_event_t *pevent /*in,out*/);

/**
 * 关闭事件，释放相关资源
 * @param event 事件句柄
 */
static inline void unios_event_close(unios_event_t event) { unios_event_destroy(&event); }


// ======================================================================================
// 统一任务管理接口
// ======================================================================================

/// @brief 任务ID类型定义(只有定时任务才有任务ID)
typedef void* unios_task_t;

/// @brief 标准任务处理器原型
typedef void (*unios_task_handler_t)(size_t cookie_size, void* cookie);

/// @brief 任务永久执行标志
#define UNIOS_TASK_REPEAT_FOREVER 0xFFFF

/**
 * 定时任务管理
 */

/// @brief 创建ms级定时任务
/// @param interval: 间隔多久执行一次，单位：ms
/// @param repeat: 执行多少次。=UNIOS_TASK_REPEAT_FOREVER 时表示永远执行，直到被清除
/// @param handler: 回调函数
/// @param len: 数据长度(data数据长度)
/// @param data: 数据体，长度=len；
/// @param fireFirstNow: 立即执行第一次
/// @retval 任务ID: =0表示失败
unios_task_t unios_task_run_every(uint32_t interval, uint32_t repeat, unios_task_handler_t handler, size_t len, const void* data, bool fireFirstNow);

/// @brief 例行执行任务: 每轮主消息循环执行一次，可认为是循环执行，直至被清除
/// @param handler: 回调函数
/// @param len: 数据长度(data数据长度)
/// @param data: 数据体，长度=len；
/// @retval 任务ID: =0表示失败
static inline unios_task_t unios_task_run_routine(unios_task_handler_t handler, size_t len, const void* data) {
	return unios_task_run_every(0, UNIOS_TASK_REPEAT_FOREVER, handler, len, data, false);
}

/// @brief 创建简单、单次ms级延时调用
/// @param delay: 延时多久执行，单位：ms
/// @param handler: 回调函数
/// @param len: 数据长度(data数据长度)
/// @param data: 数据体，长度=len；
/// @retval 任务ID: =0表示失败
static inline unios_task_t unios_task_run_after(uint32_t delay, unios_task_handler_t handler, size_t len, const void* data) {
	return unios_task_run_every(delay, 1, handler, len, data, false);
}

/// @brief DPC任务/稍后运行一次(可以从中断退出回到主程序循环后在一个单独的任务中执行)
/// @param handler: 回调函数
/// @param cookie: 用户自定义参数数据体
/// @return 标准错误码
static inline std_err_t unios_task_run_later(unios_task_handler_t handler, size_t len, const void* data) {
	return unios_task_run_every(0, 1, handler, len, data, false) ? STD_ERR_OK : STD_ERR_FAIL;
}

/// @brief 创建 RTC 定时任务
/// @param rtc_clock: 任务计划执行的时间
/// @param repeat: 执行多少次。=MAXU32 时表示永远执行，直到被清除
/// @param handler: 回调函数
/// @param len: 数据长度(data数据长度)
/// @param data: 数据体，长度=len；
/// @retval 任务ID: =0表示失败
unios_task_t unios_task_run_at(unios_time_t rtc_clock, uint32_t repeat, unios_task_handler_t handler, size_t len, const void* data);

/**
 * 消息驱动的 任务管理
 * - RX 消息处理任务: 用户可以为接收消息类型(@see unios_task_type_t)，注册接收消息处理函数，一旦有通信接口(@see transceiver_t)收到消息并发布到消息队列(@see unios_task_dispatch_rx_msg)，系统就会调用对应的处理函数进行处理。
 * - TX 消息处理任务: 用户可以将要通过通信接口(@see transceiver_t)发送的数据发布到消息队列(@see unios_task_dispatch_tx_msg)，系统会自动调用 transceiver_t 的接口发送数据，并且可支持重复发送。
 */

/// @brief 接收消息类型
typedef uint32_t unios_task_rxmsg_type_t;

/// @brief 接收消息处理器函数类型声明
/// @param cookie: 接收消息处理器cookie
/// @param receiver: 数据接收者(是谁接收到的数据，具体是什么含义，由用户自己定义)
/// @param len: 数据长度(data数据长度)
/// @param data: 数据体，长度=len；
/// @note 注意: data 中的数据是由系统分配的内存，会在消息处理结束后，由系统自动释放！
typedef void (*unios_task_rxmsg_handler_t)(void* cookie, void* receiver, size_t len, void* data);

/// @brief 接收消息处理器登记表
typedef struct _unios_task_rxmsg_handler_reg {
	unios_task_rxmsg_type_t msgType;
	unios_task_rxmsg_handler_t handler;
	void* cookie;
} unios_task_rxmsg_handler_reg_t;

typedef struct _unios_task_rxmsg_handler_map {
	size_t count;
	unios_task_rxmsg_handler_reg_t* items;
} unios_task_rxmsg_handler_map_t;

/// @brief 静态分配 接收消息处理器登记表 内存空间
#define UniosRxMsgMapAllocStatic(NAME, N) \
	extern unios_task_rxmsg_handler_reg_t NAME##_ITEMS[N];                \
	static unios_task_rxmsg_handler_map_t NAME##_MAP = {N, NAME##_ITEMS}; \
	unios_task_rxmsg_handler_map_t* NAME = &NAME##_MAP;                   \
	unios_task_rxmsg_handler_reg_t  NAME##_ITEMS[N]

/// @brief 注册接收消息处理器: 如果指定类型的消息已经注册过，则会覆盖原来的注册项 !
/// @param msgType: 消息类型
/// @param handler: 接收消息处理器
/// @param cookie: 接收消息处理器cookie
/// @return 标准错误码。
std_err_t unios_task_register_rxmsg_handler(unios_task_rxmsg_type_t msgType, unios_task_rxmsg_handler_t handler, void *cookie);
	
/// @brief 取消注册接收消息处理器: 只有 handler 和 cookie 都对得上，才取消相应的注册
/// @param handler: 接收消息处理器
/// @param cookie: 接收消息处理器cookie
void unios_task_unregister_rxmsg_handler(unios_task_rxmsg_handler_t handler, void *cookie);
	
/// @brief 取消注册指定类型的接收消息处理器
/// @param msgType: 消息类型
void unios_task_unregister_rxmsg_type(unios_task_rxmsg_type_t msgType);

/// @brief 在任务队列中创建接收消息处理任务
/// @param msgType: 消息类型
/// @param receiver: 数据接收者(是谁接收到的数据), 具体类型由应用层根据 msgType 自行判定
/// @param len: 消息数据长度
/// @param data: 消息数据体(本函数会另外分配内存，并把此数据拷贝过去，所以此函数返回后，应用端应自行处理data的生命周期)
std_err_t unios_task_dispatch_rx_msg(unios_task_rxmsg_type_t msgType, void* receiver, size_t len, void* data);

/// @brief 在任务队列中创建(单次)发送消息处理任务
/// @param sender: 数据发送者(此数据要由谁发送)
/// @param len: 消息数据长度
/// @param data: 消息数据体(本函数会另外分配内存，并把此数据拷贝过去，所以此函数返回后，应用端应自行处理data的生命周期)
std_err_t unios_task_dispatch_tx_msg(transceiver_t* sender, size_t len, const void* data);

/// @brief 在任务队列中创建(多次)发送消息处理任务
/// @param sender: 数据发送者(此数据要由谁发送)
/// @param cookie: 数据发送者 cookie
/// @param interval: 发送间隔时间(ms)
/// @param repeat: 数据要重复发送的次数
/// @param len: 消息数据长度
/// @param data: 消息数据体(本函数会另外分配内存，并把此数据拷贝过去，所以此函数返回后，应用端应自行处理data的生命周期)
/// @param fireFirstNow: 立即执行第一次
/// @retval 任务ID
unios_task_t unios_task_dispatch_tx_msg_xn(transceiver_t* sender, uint32_t interval, uint32_t repeat, size_t len, const void* data, bool fireFirstNow);

//
// 信号 驱动的任务管理
//

/// 系统信号
typedef enum {
	UNIOS_SIGNAL_0 = 0,
	UNIOS_SIGNAL_1,
	UNIOS_SIGNAL_2,
	UNIOS_SIGNAL_3,
	UNIOS_SIGNAL_4,
	UNIOS_SIGNAL_5,
	UNIOS_SIGNAL_6,
	UNIOS_SIGNAL_7,
	UNIOS_SIGNAL_8,
	UNIOS_SIGNAL_9,
	UNIOS_SIGNAL_10,
	UNIOS_SIGNAL_11,
	UNIOS_SIGNAL_12,
	UNIOS_SIGNAL_13,
	UNIOS_SIGNAL_14,
	UNIOS_SIGNAL_15,
	UNIOS_SIGNAL_16,
	UNIOS_SIGNAL_17,
	UNIOS_SIGNAL_18,
	UNIOS_SIGNAL_19,
	UNIOS_SIGNAL_20,
	UNIOS_SIGNAL_21,
	UNIOS_SIGNAL_22,
	UNIOS_SIGNAL_23,
	UNIOS_SIGNAL_24,
	UNIOS_SIGNAL_25,
	UNIOS_SIGNAL_26,
	UNIOS_SIGNAL_27,
	UNIOS_SIGNAL_28,
	UNIOS_SIGNAL_29,
	UNIOS_SIGNAL_30,
	UNIOS_SIGNAL_31,
	UNIOS_SIGNAL_MAX
} unios_signal_t;

/// @brief 收到信号时运行
unios_task_t unios_task_run_on_signal(unios_signal_t signal, unios_task_handler_t handler, size_t len, const void* data);
/// @brief 触发信号
std_err_t unios_task_fire_signal(unios_signal_t signal);

//
// 任务系统管理
//

/// @brief 清除所有任务
void unios_task_clear_all();
/// @brief 重启任务定时，从当前时间开始算
/// @param task: 任务ID: 适用于 UNIOS_TASK_MS_TIMER, UNIOS_TASK_RTC_TIMER
void unios_task_reset_timer(unios_task_t task);
/// @brief 清除ms定时任务
/// @param taskId: 任务ID
void unios_task_kill(unios_task_t task);
/// @brief 清除 ms 定时任务，并销毁任务指针
/// @param taskId: 任务ID
static inline void unios_task_destroy(unios_task_t *task) { if(*task) unios_task_kill(*task); *task = 0; }

// forward 声明
typedef struct _shift_block_queue shift_block_queue_t;

/// @brief 任务系统初始化
std_err_t unios_task_system_init(shift_block_queue_t *taskqueue, unios_task_rxmsg_handler_map_t *rxmsgmap);
/// @brief 任务系统循环步进
/// @note 注意: 一般在主系统循环中调用此函数 !!!
void unios_task_system_loop_step();

// 任务系统测试及示例程序
int unios_task_test(int argc, const char **argv);

#ifdef __cplusplus
}
#endif
